package com.tys.algorithm.beginner;

public class Code32_MergeSort {


    //非递归方法实现精简版
    public static void mergeSort3(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }

        int N = arr.length;
        int mergeSize = 1;
        while (mergeSize < N) { //步长小于总长继续
            int L = 0;
            while (L < N) { //左组位置小于总长继续
                if (mergeSize >= N - L) { //步长大于剩余数量跳出循环
                    break;
                }
                int M = L + mergeSize - 1; //中间位置
                int R = M + Math.min(mergeSize, N - M - 1); //一个步长，或者是剩余的个数
                merge(arr, L, M, R); //合并
                L = R + 1; //下一组
            }
            if (mergeSize > N / 2) {
                break;
            }
            mergeSize <<= 1; //mergeSize *= 2
        }
    }

    //非递归方法实现
    private static void mergeSort2(int[] arr) {
        //不用排序的情况
        if (arr == null || arr.length < 2) {
            return;
        }

        //步长
        int step = 1;
        int N = arr.length;
        while (step < N) { //步长大于总长度停止
            //左组起始都是0
            int L = 0;
            while (L < N) {
                int M = 0; // 中间位置
                //左组边界
                //int M = Math.min(N-1, L+step-1); 这个没考虑L+step越界
                //左组范围：L ... N-1, 剩余个数：N-1-L+1=N-L
                //判断剩余个数能否够凑够一个步长
                if (N - L >= step) {
                    M = L + step - 1; //能凑够
                } else {
                    M = N - 1; //凑不够
                }

                //右组边界
                //右组范围：M+1 ... N-1, 剩余个数：(N-1)-(M+1)+1=N-1-M
                int R = 0;
                //判断剩余个数能否够凑够一个步长
                if (N - 1 - M >= step) {
                    R = M + step; //能凑够
                } else {
                    R = N - 1;
                }

                //合并L ... M M+1 ... R
                merge(arr, L, M, R);

                //跳左组
                if (R == N - 1) {
                    break; //最后了
                } else {
                    L = R + 1;//跳到下一组
                }

                //左组凑不够，则右组没有
                if (M == N - 1) {
                    break;
                }
            }

            //防止溢出，N/2是向下取整
            if (step > N / 2) {
                break;
            }
            //调整步长
            step *= 2;
        }
    }

    //递归方法实现
    public static void mergeSort1(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }

        process(arr, 0, arr.length - 1);
    }

    //递归函数
    private static void process(int[] arr, int L, int R) {
        //只有一个数时
        if (L == R) {
            return;
        }

        //找到中间位置
        //int mid = (L + R) / 2 下标很大时，L+R可能越界
        int mid = L + ((R - L) >> 1);
        //左侧递归排序
        process(arr, L, mid);
        //右侧递归排序
        process(arr, mid + 1, R);
        //合并排好序的左右两边
        merge(arr, L, mid, R);
    }

    //合并函数
    private static void merge(int[] arr, int L, int M, int R) {
        //临时数组存储合并后的数据
        int[] help = new int[R - L + 1];
        int i = 0;
        int p1 = L;  //左指针起点
        int p2 = M + 1; //右指针起点
        //对比左右指针
        while (p1 <= M && p2 <= R) {
            help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }
        //要么P1越界，要么P2越界，不可能同时越界
        //P2越界,P1还有数据
        while (p1 <= M) {
            help[i++] = arr[p1++];
        }
        //P1越界，P2还有数据
        while (p2 <= R) {
            help[i++] = arr[p2++];
        }
        //拷贝数据到原数组中
        for (i = 0; i < help.length; i++) {
            arr[L + i] = help[i];
        }
    }

    //产生一个随机数组
    public static int[] generateRandomArray(int maxSize, int maxValue) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }
        return arr;
    }

    //拷贝数组
    public static int[] copyArray(int[] arr) {
        if (arr == null) {
            return null;
        }
        int[] res = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            res[i] = arr[i];
        }
        return res;
    }

    //对比数组是否相同
    public static boolean isEqual(int[] arr1, int[] arr2) {
        //一个null一个不为null时返回false
        if ((arr1 == null && arr2 != null) || arr1 != null && arr2 == null) {
            return false;
        }

        //都是null，返回true
        if (arr1 == null && arr2 == null) {
            return true;
        }

        //长度不相同时返回false
        if (arr1.length != arr2.length) {
            return false;
        }

        //对比内容
        for (int i = 0; i < arr1.length; i++) {
            if (arr1[i] != arr2[i]) {
                return false;
            }
        }

        return true;
    }

    //打印数组
    public static void printArray(int[] arr) {
        if (arr == null) {
            return;
        }

        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int testTime = 500000;
        int maxSize = 100;
        int maxValue = 100;
        System.out.println("测试开始");
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = generateRandomArray(maxSize, maxValue);
            int[] arr2 = copyArray(arr1);
            int[] arr3 = copyArray(arr1);
            mergeSort1(arr1);
            mergeSort2(arr2);
            mergeSort3(arr3);
            if (!isEqual(arr1, arr2)) {
                System.out.println("出错了！");
                printArray(arr1);
                printArray(arr2);
                break;
            }

            if (!isEqual(arr1, arr3)) {
                System.out.println("出错了！");
                printArray(arr1);
                printArray(arr3);
                break;
            }
        }
        System.out.println("测试结束");
    }


}
